package com.hmall.search.service.impl;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.hmall.common.PageBean;
import com.hmall.feign.client.ItemClient;
import com.hmall.item.pojo.Item;
import com.hmall.search.constants.ItemConstants;
import com.hmall.search.dto.SearchDto;
import com.hmall.search.service.SearchService;
import lombok.SneakyThrows;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.alibaba.fastjson.JSON;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SearchServiceImpl implements SearchService {
    @Autowired
    ItemClient itemClient;
    //注入es核心对象
    @Autowired
    RestHighLevelClient client;


    @Override
    public PageBean<Item> pageQuery(Integer page, Integer size, String sortBy, String category, String brand) {
        return null;
    }

    @Override
    @SneakyThrows //捕获异常
    public PageBean<Item> search(SearchDto dto) {
        //1. 创建搜索请求对象
        SearchRequest request = new SearchRequest("item");

        //2. 通过request对象设置查询条件
        BoolQueryBuilder boolQuery = new BoolQueryBuilder();

        boolQuery.must(QueryBuilders.matchAllQuery()); //查询所有

        //TODO 封装各种各样的查询条件
        //TODO 封装各种各样的查询条件
        //TODO 封装各种各样的查询条件
        //判断，如果条件有值，将条件封装到 request 对象中

        //搜索关键字
        if (StrUtil.isNotBlank(dto.getKey())) {
            boolQuery.must(QueryBuilders.matchQuery("all", dto.getKey()));
        }

        //品牌
        if (StrUtil.isNotBlank(dto.getBrand())) {
            boolQuery.filter(QueryBuilders.termQuery("brand", dto.getBrand()));
        }

        //分类
        if (StrUtil.isNotBlank(dto.getCategory())) {
            boolQuery.filter(QueryBuilders.termQuery("category", dto.getCategory()));
        }

        //价格  min  <= price  <=  max     es索引库价格单位是分
        if (ObjectUtil.isAllNotEmpty(dto.getMinPrice(), dto.getMaxPrice())) {
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getMinPrice() * 100).lte(dto.getMaxPrice() * 100));
        }
        FunctionScoreQueryBuilder functionScoreQuery = QueryBuilders
                .functionScoreQuery(boolQuery, new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAD", true),
                                ScoreFunctionBuilders.weightFactorFunction(10))
                })
                .boostMode(CombineFunction.MULTIPLY);
        //排序

        if(dto.getSortBy().equals("default")) {
            request.source()
                    .query(boolQuery)
                    .from((dto.getPage() - 1) * dto.getSize())   //第一页：0   第二页：5  第三页：15
                    .size(dto.getSize())
                    .highlighter(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>").requireFieldMatch(false))
                    .sort("id", SortOrder.DESC);
        } else if(dto.getSortBy().equals("price")){
            request.source()
                    .query(boolQuery)
                    .from((dto.getPage() - 1) * dto.getSize())   //第一页：0   第二页：5  第三页：15
                    .size(dto.getSize())
                    .highlighter(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>").requireFieldMatch(false))
                    .sort("price", SortOrder.DESC);
            System.out.println("price排序");
        }else if(dto.getSortBy().equals("sold")){
            request.source()
                    .query(boolQuery)
                    .from((dto.getPage() - 1) * dto.getSize())   //第一页：0   第二页：5  第三页：15
                    .size(dto.getSize())
                    .highlighter(new HighlightBuilder().field("name").preTags("<em>").postTags("</em>").requireFieldMatch(false))
                    .sort("sold", SortOrder.DESC);
        }

        //3. 通过client对象，执行
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //4. 处理结果
        SearchHit[] hits = response.getHits().getHits();

        //map：映射，将A->B    SearchHit->Item
        List<Item> list = Arrays.stream(hits).map(searchHit -> {
            Item itemDoc = JSON.parseObject(searchHit.getSourceAsString(), Item.class);
            //处理高亮
            HighlightField highlightField = searchHit.getHighlightFields().get("name");
            if (highlightField != null) {
                String name = highlightField.fragments()[0].toString();
                itemDoc.setName(name);
                System.out.println("高亮:");
            }
            return itemDoc;
        }).collect(Collectors.toList());
        long total = response.getHits().getTotalHits().value;
        return new PageBean<>(total, list);
    }

    @Override
    public void mysql2ES() throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest("item");
        boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);

        //如果存在，把索引库删掉
        if (exists) {
            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("item");
            client.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        }

        //1. 创建索引请求对象
        CreateIndexRequest createIndexRequest = new CreateIndexRequest("item");
        //设置索引的字段类型
        createIndexRequest.source(ItemConstants.JSON, XContentType.JSON);
        //2. 执行请求对象
        client.indices().create(createIndexRequest, RequestOptions.DEFAULT);


        //项目启动时执行（只执行一次）
        //1. 获取到mysql数据库中的数据（通过feign接口）
        PageBean<Item> pageBean = itemClient.queryItemByPage(1, 1000);
        List<Item> list = pageBean.getList();

        //2. 遍历集合，将每一个Item对象添加到es中
        for (Item item : list) {
            //1. 创建文档请求对象
            IndexRequest request = new IndexRequest("item");
            //设置索引的字段类型
            request.id(String.valueOf(item.getId()));
            request.source(JSON.toJSONString(item), XContentType.JSON);

            //2. 执行请求对象
            client.index(request, RequestOptions.DEFAULT);
        }
    }
}
